//==============================================================================
// ParseUtils.cc
//----------------------------------------------------------------------------
// Some utility functions to simplify some common string and file 
// parsing operations.   
//
//------------------------------------------------------------------------------
// $Id: ParseUtils.cc,v 1.1 2000/01/31 21:14:54 kdurbin Exp kdurbin $
//------------------------------------------------------------------------------
// $Log: ParseUtils.cc,v $
// Revision 1.1  2000/01/31 21:14:54  kdurbin
// Initial revision
//
//   
//==============================================================================
//.......1.........2.........3.........4.........5.........6.........7.........8
//

#include "ParseUtils.hh"

// Character sets for searching with find_first_of and find_first_not_of
const string gWS=" \t\n";
const string gNumbers="1234567890";
const string gLetters="abcdefghijklmnopqrstuvwxyz";
const string gUCLetters="ABCDEFGHIJKLMNOPQRSTUVWXYZ";

//---------------------------------------------------------------------------
// * ShowHidden
//---------------------------------------------------------------------------
// Convenience function for debugging. 
//
void ShowHidden(string &line,ostream &fout)
{
  for(int i = 0;i< line.size();i++){
	 fout<<i<<":";
	 if (line[i] == ' ') fout<<"SPACE"<<endl;
	 else if (line[i] == '\t') fout<<"TAB"<<endl;
	 else if (line[i] == '\n') fout<<"NEWLINE"<<endl;
	 else fout<<line[i]<<endl;
  }
}


//---------------------------------------------------------------------------
// * Split
//---------------------------------------------------------------------------
// Ever so vaguely similar to Perl split command. Takes a string and returns a 
// vector of the bits separated by delimited.  It takes a string delimiter 
// so that you can have delimiters like "->".  
//
void Split(string &inString,vector<string> &outVec,string delimiter)
{
  int idx0,idx1;
  int dsize = delimiter.size();
  outVec.clear();

  // Get the first one.
  idx0 = inString.find(delimiter);
  if (idx0 >=0){
	 outVec.push_back(inString.substr(0,idx0));
  }else return;
						
  idx0 = idx0+dsize; // move to one beyond the delimiter
  do{
	 idx1 = inString.find(delimiter,idx0);
	 if ((idx1>=0) && (idx1 > idx0)){
		outVec.push_back(inString.substr(idx0,idx1-idx0));
	 }else {
		// Last one, return till end. 
		outVec.push_back(inString.substr(idx0,inString.size()-idx0));
		return;
	 }

	 idx0 = idx1+dsize;  
  }while(idx0 >=0);
}


//---------------------------------------------------------------------------
// * CutString
//---------------------------------------------------------------------------
// Ever so vaguely similar to the unix cut command.  Takes a string divided
// into fields by the delimiter character, and returns the substring corresponding
// to the specified field. 
//
bool CutString(string &inString,string &outString,int field,char delimiter)
{

  // Find the appropriate start and end location.
  int didx = -1;
  int foundcount = 0;
  int lastdidx = didx;
  for(int i = 0;i< field;i++){
	 lastdidx = didx;
	 didx = inString.find(delimiter,didx+1);
	 if (didx >=0) foundcount++;		
	 else break; // No more delimiters
  }

  // Special case for the first field. There is always a first field, 
  // either one followed by a delimiter (foundcount==1) or not.  
  if (field == 1){
	 if (foundcount==1){
		outString = inString.substr(0,didx);
		return(true);
	 }else{
		outString = inString;
		return(true);
	 }
  }
	
  // Special case where field is last field in the string. 
  if (foundcount == (field-1)){
	 outString = inString.substr(lastdidx+1,inString.size()-didx-1);
	 return(true);
  }else{ 
	 // If it's not the last field of the string then field must equal foundcount.
	 if (foundcount == field){
		outString = inString.substr(lastdidx+1,didx-lastdidx-1);
		return(true);
	 }else{
		outString = "";
		return(false);
	 }
  }
}



//---------------------------------------------------------------------------
// * StripLeadingSpaces
//---------------------------------------------------------------------------
//
//
void StripLeadingSpaces(string &theLine)
{
  string temp;
  int i =0;
  while((theLine[i] == ' ')||(theLine[i]=='\t')||(theLine[i]=='\n')){
	 i++;
  }
  for(int j= i;j<theLine.size();j++){
	 temp+=theLine[j];
  }
  theLine = temp;
}


//---------------------------------------------------------------------------
// * StripTrailingWhiteSpace
//---------------------------------------------------------------------------
// Remove all of the whitespace from end of a  string. 
//
void StripTrailingSpace(string &theLine)
{
	register int wscount = 0;
	register int startSize = theLine.size();
	register char base;

  for(int i= startSize-1;i>=0;i--){
		base = theLine[i];
		if ((base == ' ') || (base =='\t') || (base == '\n')){
			wscount++;
		}else break;
  }

	int newSize = startSize - wscount;
	theLine.resize(newSize);
}



//---------------------------------------------------------------------------
// * StripWhiteSpace
//---------------------------------------------------------------------------
// Remove all of the whitespace from a string. 
//
void StripWhiteSpace(string &theLine)
{
  string temp;

  for(int i= 0;i<theLine.size();i++){
	 if ((theLine[i] != ' ')&&(theLine[i]!='\t')&&(theLine[i]!='\n')){
		temp+=theLine[i];
	 }
  }
  theLine = temp;
}


//---------------------------------------------------------------------------
// * StartsWith
//---------------------------------------------------------------------------
// Returns true/false if source contains/doesn't contain query. 
// 
bool StartsWith(string &source,string &query)
{
  int idx = source.find(query);
  if (idx>=0){
		if(idx == 0){
			return(true);
		}else return(false);
  }else return(false);
}


//---------------------------------------------------------------------------
// * StartsWith
//---------------------------------------------------------------------------
// Returns true/false if source contains/doesn't contain query. 
// 
bool StartsWith(string &source,char * qcstr)
{
  string query = qcstr;
  int idx = source.find(query);
  if (idx>=0){
	 // It's in the string, is it at the start? 	 
	 if(idx == 0){
		 return(true);
	 }else return(false);
  }else return(false);
}


//---------------------------------------------------------------------------
// * Contains
//---------------------------------------------------------------------------
// Returns true/false if source contains/doesn't contain query. 
// 
bool Contains(string &source,string &query)
{
  int idx = source.find(query);
  if (idx>=0) return(true);
  else return(false);
}

//---------------------------------------------------------------------------
// * Contains
//---------------------------------------------------------------------------
// Returns true/false if source contains/doesn't contain query. 
//
bool Contains(string &source,char* query )
{
  string qString = query;
  int idx = source.find(qString);
  if (idx>=0) return(true);
  else return(false);
}

//---------------------------------------------------------------------------
// * ContainsOnlyWS
//---------------------------------------------------------------------------
// Checks to see if a string contains only white space.  Useful if you 
// are reading in info from a file which occasionally contains blank lines. 
// 
bool ContainsOnlyWS(string &s)
{
  int offset = 0;
  int Idx = s.find_first_not_of(gWS,offset);
  if (Idx < 0) return(true);
  else return(false);
}


//---------------------------------------------------------------------------
// * FindNextOf
//---------------------------------------------------------------------------
// Returns the start and end location of the Next occurence of a keyword in 
// KeywordList after position offset.  Also returns the index of that 
// keyword in the list. 
//  
//
int
FindNextOf(vector<string> &KeywordList,string &Source,
						  int offset,
						  int &wordStart,
						  int &wordEnd)
{
	wordStart = -1;
	wordEnd = -1;
	int keyIdx = 0;
	for(int i = 0;i<KeywordList.size();i++){
		int idx = Source.find(KeywordList[i],offset);
		if (idx >=0){
			// Want to return the index of the -earliest- keyword after offset
			if ((idx < wordStart) || (wordStart <0)){
				wordStart = idx;
				wordEnd = idx+KeywordList[i].size()-1;
				keyIdx = i;
			}
		}
	}

	// Return the index of the earliest keyword in the list. 
	return(keyIdx);
}

//---------------------------------------------------------------------------
// * GetNextKeyword
//---------------------------------------------------------------------------
// Returns the Next keyword from KeywordList that occurs in Source.  
// This may seem like an unlikely function, but it turns out to be 
// very handy for parsing files when you expect one of a set of 
// keywords to turn up but can't be exactly sure which will be next.     
//
bool
GetNextKeyword(vector<string> &KeywordList,
				string &Source,int offset,int &newoffset,string &word)
{
	int start,end;
	FindNextOf(KeywordList,Source,offset,start,end);
	if (start >= 0){
		newoffset = end+1;
		word = Source.substr(start,(end-start+1));
		return(true);
	}
	newoffset =-1;
	return(false);
}

bool
ContainsOnlyDigits(string &line,int start, int end)
{
  int firstNonNumIdx = line.find_first_not_of(gNumbers,start);
  if (firstNonNumIdx <= end) return(false);
  else return(true);
}



//---------------------------------------------------------------------------
// * FindNextInteger
//---------------------------------------------------------------------------
// Scans Source for the Next occurence of an integer.  This function 
// will treat a floating point number nnnnn.NNNNN as two separate 
// integers nnnnn and NNNNN separated by a period. That is, this 
// function finds the Next number after offset and interprets it 
// -as- an integer.  It DOES NOT find the Next number which IS an 
// integer, skipping floats or some such. 
// 
int
FindNextInteger(string &Source,int offset,int &start,int &end)
{
	start = -1; end = -1;
	int numIdx1 = Source.find_first_of(gNumbers,offset);
	if (numIdx1 >=0){
		start = numIdx1;
		int numIdx2 = Source.find_first_not_of(gNumbers,numIdx1+1);
		if (numIdx2 >=0){
			end = numIdx2-1;
		}else{
			end = Source.size();
		}
		return(0);
	}
	return(-1);
}

//---------------------------------------------------------------------------
// * FindNextDouble
//---------------------------------------------------------------------------
// Finds the Next occurence of a number that will be interpreted as
// a double.  This includes numbers in the format nnnn, nnnnn.NNNNN 
// .NNNNNN or nnn.NNNNNe-xxxx.   That is, this function finds the 
// Next number after offset and tries to interpret it -as- a double. 
// This function DOES NOT find the Next number which IS a double. 
// 
int
FindNextDouble(string &Source,int offset,int &start,int &end)
{
  int status1=0,status2=0,start2=-1,end2=-1;

  status1 = FindNextInteger(Source,offset,start,end);
  if (status1>=0){
	 // If there is a decimal place, get the remaining integer part.
	 if ((end+1)<Source.size()){
		if(Source[end+1] == '.'){
		  status2 = FindNextInteger(Source,end+2,start2,end2);
		  // If an integer was found, adjacent to the ".", then extend the
		  // end marker to include it. 
		  if ((status2 >=0) && (start2 == (end+2))){
			 end = end2;
		  }
		}
	 }
	 
	 // If there is an exponent, pick that up
	 if ((end+1)<Source.size()){
		if(tolower(Source[end+1]) == 'e'){
		  status2 = FindNextInteger(Source,end+2,start2,end2);
		  if (status2 >=0){
			 end = end2;
		  }
		}
	 }
	 return(status1);
  }
  return(status1);
}

//---------------------------------------------------------------------------
// * GetNextLong
//---------------------------------------------------------------------------
// Returns the Next integer in Source after position offset.  The position
// in the string immediately after offset is returned in newoffset.  A 
// series of integers on a line can be returned with a sequence of calls 
// such as:
//
// num1 = GetNextInteger(s1,0,newoffset);
// num2 = GetNextInteger(s1,newoffset,newoffset);
// num3 = GetNextInteger(s1,newoffset,newoffset);
//
// newoffset returns -1 if there are no more numbers on the line. 
//
// KJD:  Shouldn't this be GetNextInteger???
//
int
GetNextLong(string &Source,int offset,int &newoffset)
{
	int start=0,end=0;
	FindNextInteger(Source,offset,start,end);
	if (start >=0){
		newoffset = end+1;
		string numStr = Source.substr(start,(end-start+1));
		long num = atol(numStr.c_str());	
		if (start >0){
		  if (Source[start-1] == '-'){
			 num = -num;
		  }
		}
		return(num);
	}
	newoffset = -1;
	return((long)0);	
}


//---------------------------------------------------------------------------
// * GetNextInteger
//---------------------------------------------------------------------------
// Returns the Next integer in Source after position offset.  The position
// in the string immediately after offset is returned in newoffset.  A 
// series of integers on a line can be returned with a sequence of calls 
// such as:
//
// num1 = GetNextInteger(s1,0,newoffset);
// num2 = GetNextInteger(s1,newoffset,newoffset);
// num3 = GetNextInteger(s1,newoffset,newoffset);
//
// newoffset returns -1 if there are no more numbers on the line. 
//
// KJD:  Shouldn't this be GetNextInteger???
//
int
GetNextInteger(string &Source,int offset,int &newoffset)
{
	int start=0,end=0;
	FindNextInteger(Source,offset,start,end);
	if (start >=0){
		newoffset = end+1;
		string numStr = Source.substr(start,(end-start+1));
		int num = atoi(numStr.c_str());	
		if (start >0){
		  if (Source[start-1] == '-'){
			 num = -num;
		  }
		}
		return(num);
	}
	newoffset = -1;
	return(0);	
}


//---------------------------------------------------------------------------
// * GetNextDouble
//---------------------------------------------------------------------------
// Returns the Next double in Source after position offset.  The position
// in the string immediately after offset is returned in newoffset.  A 
// series of floats on a line can be returned with a sequence of calls 
// such as:
//
// num1 = GetNextDouble(s1,0,newoffset);
// num2 = GetNextDouble(s1,newoffset,newoffset);
// num3 = GetNextDouble(s1,newoffset,newoffset);
//
// newoffset returns -1 if there are no more numbers on the line. 
//
double
GetNextDouble(string &Source,int offset,int &newoffset)
{
	int start=0,end=0;
	int status = FindNextDouble(Source,offset,start,end);
	if (status >=0){
		newoffset = end+1;
		string numString;
		if (start>0){
		  // see if it's negative, and if so grab the - sign as well. 
		  if (Source[start-1] == '-'){
			 numString = Source.substr(start-1,(end-start+1));
		  }else{
			 numString = Source.substr(start,(end-start+1));
		  }
		}else{
		  numString = Source.substr(start,(end-start+1));
		}
		double temp;
		sscanf(numString.c_str(),"%lf",&temp);
		return(temp);
	}	
	newoffset = -1;
	return(0.0);	
}

int
GetAllDoubles(string &Line,vector<double> &AllDoubles)
{
  int newoffset = 0;
  do{
	 double nextDouble = GetNextDouble(Line,newoffset,newoffset);
	 if (newoffset >= 0){
		 AllDoubles.push_back(nextDouble);
	 }
  }while(newoffset > 0);

  return(AllDoubles.size());
}


int
GetAllIntegers(string &Line,vector<int> &AllInts)
{
  int newoffset = 0;
  do{
	 int nextInt = GetNextInteger(Line,newoffset,newoffset);
	 if (newoffset >= 0){
		AllInts.push_back(nextInt);
	 }
  }while(newoffset > 0);

  return(AllInts.size());

}



//---------------------------------------------------------------------------
// * GetNextWSDelimitedString
//---------------------------------------------------------------------------
// Returns the string delimited by white space.  End of line is treated as ws. 
//
string
GetNextWSDelimitedString(string &Source,int offset, int &newoffset)
{
  string temp = "";
  // find the first character that is not white space
  int start = Source.find_first_not_of(gWS,offset);
  if (start >=0){
	 offset = start+1;
	 // if (offset < Source.size()){
	 int end = Source.find_first_of(gWS,offset);
	 // A positive index means an actual WS found. 
	 if (end >=0){
		temp = Source.substr(start,(end-start));
		newoffset = end+1;
	 }else{
		// Since we found a valid start, there is always an end, if only 
		// the end of the string.  A negative end implies end of the string. 
		temp = Source.substr(start,Source.size()-start);
		//newoffset= Source.size()-1;
		newoffset = -1;
	 }
	 return(temp);
	 //}else{
	 //	newoffset = -1;
	 //	return(temp);
	 // }
  }else{
	 newoffset = -1;
	 return(temp);
  }
}

//---------------------------------------------------------------------------
// * GetNextLineContaining
//---------------------------------------------------------------------------
// Reads in a stream until a line containing the given text is found. 
//
//
bool GetNextLineContaining(istream &inFile,string &feature,
									string &tempLine)
{
  int idx;
  bool bFeatureMarker;
  do{
	 GetLine(inFile,tempLine);
	 idx = tempLine.find(feature);
	 bFeatureMarker = (idx>=0);
  }while((!inFile.eof()) && (!bFeatureMarker));

  if (!bFeatureMarker) return(false);
  else return(true);
}

//---------------------------------------------------------------------------
// * GetNextLineContaining
//---------------------------------------------------------------------------
// Reads in a stream until a line containing the given text is found. 
//
//
bool GetNextLineContaining(istream &inFile,const char*featureName,
									string &tempLine)
{
  int idx;
  bool bFeatureMarker;
  string feature = featureName;
  do{
	 GetLine(inFile,tempLine);
	 idx = tempLine.find(feature);
	 bFeatureMarker = (idx>=0);
  }while((!inFile.eof()) && (!bFeatureMarker));

  if (!bFeatureMarker) return(false);
  else return(true);

}

//---------------------------------------------------------------------------
// * GetNextLineContaining
//---------------------------------------------------------------------------
// Finds the next line containing fName1 or fName2.  (Precursor to 
// vector<string> version below).  
//
bool GetNextLineContaining(istream &inFile,const char*fName1,const char* fName2,
									string &tempLine)
{
  int idx;
  bool bFeatureMarker;
  string feature1 = fName1;
  string feature2 = fName2; 
  do{
	 GetLine(inFile,tempLine);
	 idx = tempLine.find(feature1);
	 bFeatureMarker = (idx>=0);
	 // If that feature isn't there, look for the other
	 if (!bFeatureMarker){
		idx = tempLine.find(feature2);
		bFeatureMarker = (idx>=0);
	 }
  }while((!inFile.eof()) && (!bFeatureMarker));

  if (!bFeatureMarker) return(false);
  else return(true);

}

//---------------------------------------------------------------------------
// * GetNextLineContaining
//---------------------------------------------------------------------------
//
//
bool GetNextLineContaining(istream &inFile,vector<string>&KeywordList,
								  string &tempLine,int &keyIdx)
{
  int idx;
  bool bFeatureMarker;
  do{
	 GetLine(inFile,tempLine);

	 // Search for the occurence of any of the given keywords.
	 int wordStart=0,wordEnd=0;
	 keyIdx = FindNextOf(KeywordList,tempLine,0,wordStart,wordEnd);

	 bFeatureMarker = (wordStart >=0);

  }while((!inFile.eof()) && (!bFeatureMarker));

  if (!bFeatureMarker) return(false);
  else return(true);
}

//---------------------------------------------------------------------------
// * GetRemainderAfter
//---------------------------------------------------------------------------
// Returns the remainder of a string occuring after the given feature. 
//
//
void GetRemainderAfter(const char* featureName,string &tempLine,string &outName)
{
  string copy = tempLine;
  string feature = featureName;
  int idx = tempLine.find(feature);
  idx = idx+feature.size()-1;
  if (idx >=0){
	 int size = tempLine.size();
	 outName = tempLine.substr(idx+1,size-(idx+1));
  }else{
	 outName = "";
  }
}

//---------------------------------------------------
// * GetIntsFromLine
//---------------------------------------------------
// Populates a vector with integers found on a whitespace delimited
// line. 
//
void GetIntsFromLine(char *buffer,vector<int> &Values)
{ 
  register int bLen = strlen(buffer);
  register int LeftIdx=0,RightIdx=0; 

  do{
	 // Find first non-whitespace character
	 while((LeftIdx<bLen) && ((buffer[LeftIdx] == ' ') ||
								  (buffer[LeftIdx] == '\t')||
								  (buffer[LeftIdx] == '\n'))){
		LeftIdx++;
	 }	
	 RightIdx = LeftIdx;

	 // idx points to the beginning of a number.  Now find the end
	 while((RightIdx<bLen) && (buffer[RightIdx] !=' ') &&
			 (buffer[RightIdx]!= '\t') && (buffer[RightIdx] !='\n')){
		RightIdx++;
	 }
		
	 // Convert the whitespace character that RightIdx points to to 
	 // a null
	 buffer[RightIdx]='\0'; 
	 
	 // Convert that string to an integer. 
	 Values.push_back(atoi(buffer+LeftIdx));

	 LeftIdx = RightIdx+1;
  }while(RightIdx < (bLen-1));
}


//---------------------------------------------------
// * GetIntegersFromLine
//---------------------------------------------------
// Populates a vector with integers found on a whitespace delimited
// line. 
//
void GetIntsFromLine(string Line,vector<int> &Values)
{
  string tempNum;
  string whitespace = " \t\n";
  
  int LeftIdx=0,RightIdx=0;
  
  do{
    LeftIdx = Line.find_first_not_of(whitespace,RightIdx);
    if (LeftIdx >= 0){
      RightIdx = Line.find_first_of(whitespace,LeftIdx);
      // At the end of a line, stripped of \n, have to fill 
      // in the index. 
      if (RightIdx < 0){
		  RightIdx = Line.size();
      }
      tempNum = Line.substr(LeftIdx,(RightIdx-LeftIdx));
		Values.push_back(atoi(tempNum.c_str()));
    }
  }while(LeftIdx >=0);
}

//---------------------------------------------------
// * IsBase
//---------------------------------------------------
//
bool IsBase(char a)
{
  if ( (a == 'a') || (a == 'c') || (a == 'g') || (a == 't') ||
		 (a == 'A') || (a == 'C') || (a == 'G') || (a == 'T') ||
		 (a == 'X') || (a == 'N') || (a == 'x') || (a == 'n'))
	 {
		return(true);
	 }else{
		return(false);
	 }
}


//---------------------------------------------------------------------------
// * ToLowerCase
//---------------------------------------------------------------------------
// 
void ToLowerCase(string &s)
{
  int ssize = s.size();
  for(int i = 0;i<ssize;i++){
    s[i] = tolower(s[i]);
  } 
}

//---------------------------------------------------------------------------
// * ToUpperCase
//---------------------------------------------------------------------------
// 
void ToUpperCase(string &s)
{
  int ssize = s.size();
  for(int i = 0;i<ssize;i++){
    s[i] = toupper(s[i]);
  } 
}


// Some syntatic sugar... 
// Each of the following functions is redundant, strictly speaking, 
// but the names are shorter and the interface slightly more convenient. 
// 
int    NextInt(string &Source,int &offset){
  return(GetNextInteger(Source,offset,offset));
}

long   NextLong(string &Source, int &offset){
	return(GetNextLong(Source,offset,offset));
}


double NextDouble(string &Source,int &offset){
  return(GetNextDouble(Source,offset,offset));
}

float NextFloat(string &Source,int &offset)
{
  return((float)NextDouble(Source,offset));
}

bool Cut(string &inString,string &outString,int field,char delimiter){
  return(CutString(inString,outString,field,delimiter));
}


string SplitNext(string &Source,int &offset){
  return(GetNextWSDelimitedString(Source,offset,offset));
}

string NextString(string &Source,int &offset){
  return(GetNextWSDelimitedString(Source,offset,offset));
}
